package designpattern

import (
	"fmt"
	"sync"
)

type Subject struct {
	registryMap map[string]*Observer
	lock        sync.Mutex
}

func NewSubject() *Subject {
	return &Subject{registryMap: make(map[string]*Observer)}
}

func (s *Subject) Registry(name string) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	if _, ok := s.registryMap[name]; !ok {
		s.registryMap[name] = newObserver(name)
		return nil
	} else {
		return fmt.Errorf("%s exists", name)
	}
}

func (s *Subject) Unregistry(name string) error {
	s.lock.Lock()
	defer s.lock.Unlock()

	if v, ok := s.registryMap[name]; ok {
		v.destory()
		delete(s.registryMap, name)
		return nil
	} else {
		return fmt.Errorf("%s does not exists", name)
	}
}

func (s *Subject) Notify(news string) {
	for _, v := range s.registryMap {
		v.news <- news
	}
}

type Observer struct {
	news chan string
	name string
}

func (o *Observer) listen() {
	for {
		select {
		case news, ok := <-o.news:
			if ok {
				fmt.Printf("%s receive news: %s\n", o.name, news)
			} else {
				return
			}
		}
	}

}

func (o *Observer) destory() {
	close(o.news)
}

func newObserver(name string) *Observer {
	observer := &Observer{name: name, news: make(chan string)}
	go observer.listen()
	return observer
}
